/* Microsoft Reference Implementation for TPM 2.0
 *
 *  The copyright in this software is being made available under the BSD License,
 *  included below. This software may be subject to other third party and
 *  contributor rights, including patent rights, and no such rights are granted
 *  under this license.
 *
 *  Copyright (c) Microsoft Corporation
 *
 *  All rights reserved.
 *
 *  BSD License
 *
 *  Redistribution and use in source and binary forms, with or without modification,
 *  are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this list
 *  of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice, this
 *  list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS""
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 *  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "Tpm.h"
#include "PolicyTicket_fp.h"

#if CC_PolicyTicket  // Conditional expansion of this file

#include "Policy_spt_fp.h"

/*(See part 3 specification)
// Include ticket to the policy evaluation
*/
//  Return Type: TPM_RC
//      TPM_RC_CPHASH           policy's cpHash was previously set to a different
//                              value
//      TPM_RC_EXPIRED          'timeout' value in the ticket is in the past and the
//                              ticket has expired
//      TPM_RC_SIZE             'timeout' or 'cpHash' has invalid size for the
//      TPM_RC_TICKET           'ticket' is not valid
/*
Enables policy authorization by verifying a ticket that represents a validated authorization that had an expiration time associated with it.
通过验证表示与其关联的过期时间的已验证授权的票据来启用策略授权。

This command is similar to TPM2_PolicySigned() except that it takes a ticket instead of a signed authorization. 
The ticket represents a validated authorization that had an expiration time associated with it.
The parameters of this command are checked as described in 23.2.2.
If the checks succeed, the TPM uses the timeout, cpHashA, policyRef, and authName to construct a
ticket to compare with the value in ticket. If these tickets match, then the TPM will create a TPM2B_NAME (objectName) 
using authName and update the context of policySession by PolicyUpdate() (see 23.2.3).

PolicyUpdate(commandCode, authName, policyRef)

此命令类似于 TPM2_PolicySigned（），不同之处在于它接受票证而不是签名授权。票证表示具有与其关联的过期时间的已验证授权。
按照 23.2.2 中所述检查此命令的参数。
如果检查成功，TPM 将使用超时、cpHashA、policyRef 和 authName 来构造
票证与票证中的值进行比较。如果这些票证匹配，则 TPM 将使用 authName 创建一个TPM2B_NAME（对象名称），并通过 PolicyUpdate（） 更新 policySession 的上下文（请参阅 23.2.3）。

authName: 提供授权的对象的名称
此处提供授权的对象是否是指 “签名密钥”？
tpm2_policysigned 时返回一个票据，该票据在有效时间内可以使用。（
因为policysigned时使用了密钥，表明自己持有密钥，ticket 作为持有该密钥签名的凭证，在 timeout 时间内，使用该票据证可以证明自己持有该私钥，而不需要重新验证签名。
而下次在策略验证时，可以使用票据来证明持有签名私钥

所以 TPM2_PolicyTicket 不能在使用策略会话中使用，因为它本质是使用票证代替先前给定的授权
*/

TPM_RC
TPM2_PolicyTicket(
    PolicyTicket_In     *in             // IN: input parameter list
    )
{
    TPM_RC                   result;
    SESSION                 *session;
    UINT64                   authTimeout;
    TPMT_TK_AUTH             ticketToCompare;
    TPM_CC                   commandCode = TPM_CC_PolicySecret;
    BOOL                     expiresOnReset;

// Input Validation

    // Get pointer to the session structure
    session = SessionGet(in->policySession);

    // NOTE: A trial policy session is not allowed to use this command.
    // A ticket is used in place of a previously given authorization. Since
    // a trial policy doesn't actually authenticate, the validated
    // ticket is not necessary and, in place of using a ticket, one
    // should use the intended authorization for which the ticket
    // would be a substitute.
    // 注意：不允许试用策略会话使用此命令。
    // 使用票证代替先前给定的授权。 由于试用政策实际上并不进行身份验证，
    // 因此不需要经过验证的票证，并且应该使用预期的授权来代替票证，而不是使用票证。
    if(session->attributes.isTrialPolicy)
        return TPM_RCS_ATTRIBUTES + RC_PolicyTicket_policySession;
    // Restore timeout data.  The format of timeout buffer is TPM-specific.
    // In this implementation, the most significant bit of the timeout value is
    // used as the flag to indicate that the ticket expires on TPM Reset or 
    // TPM Restart. The flag has to be removed before the parameters and ticket
    // are checked.
    if(in->timeout.t.size != sizeof(UINT64))
        return TPM_RCS_SIZE + RC_PolicyTicket_timeout;
    authTimeout = BYTE_ARRAY_TO_UINT64(in->timeout.t.buffer);

    // extract the flag
    expiresOnReset = (authTimeout & EXPIRATION_BIT) != 0;
    authTimeout &= ~EXPIRATION_BIT;

    // Do the normal checks on the cpHashA and timeout values
    result = PolicyParameterChecks(session, authTimeout,
                                   &in->cpHashA, 
                                   NULL,                    // no nonce
                                   0,                       // no bad nonce return
                                   RC_PolicyTicket_cpHashA,
                                   RC_PolicyTicket_timeout);
    if(result != TPM_RC_SUCCESS)
        return result;
    // Validate Ticket
    // Re-generate policy ticket by input parameters
    TicketComputeAuth(in->ticket.tag, in->ticket.hierarchy, 
                      authTimeout, expiresOnReset, &in->cpHashA, &in->policyRef, 
                      &in->authName, &ticketToCompare);
    // Compare generated digest with input ticket digest
    if(!MemoryEqual2B(&in->ticket.digest.b, &ticketToCompare.digest.b))
        return TPM_RCS_TICKET + RC_PolicyTicket_ticket;

// Internal Data Update

    // Is this ticket to take the place of a TPM2_PolicySigned() or
    // a TPM2_PolicySecret()?
    if(in->ticket.tag == TPM_ST_AUTH_SIGNED)
        commandCode = TPM_CC_PolicySigned;
    else if(in->ticket.tag == TPM_ST_AUTH_SECRET)
        commandCode = TPM_CC_PolicySecret;
    else
        // There could only be two possible tag values.  Any other value should
        // be caught by the ticket validation process.
        FAIL(FATAL_ERROR_INTERNAL);

    // Update policy context
    PolicyContextUpdate(commandCode, &in->authName, &in->policyRef,
                        &in->cpHashA, authTimeout, session);

    return TPM_RC_SUCCESS;
}

#endif // CC_PolicyTicket